/*This is a example of the message type takes from my Software Engineering Teamwork, 
and this work is implemented by myself. So I Use this file as an example. 

First of all, the class messageType must implement the member function "process",
since the thread pool use this function to process affairs; I use json to take data, 
you can use another way you want. It is up to your implementation.

Second, if you need to transfer some data back to the client, you can use send() to transfer
data. But you must notice that if the data you want to transfer is large enough (such as 1MB),
the send() call will not be successful, and it may set the errno to EAGAIN. So, in this case,
the right way to deal with this problem is using the EPOLLOUT event. When EPOLLOUT event is triggered,
you can send data you have not sent. A common implementation is that using a global set(such as map 
or unordered_map to store fd and it's data have not sent, and register the EPOLLOUT event to the fd.
then the data will be sent on the loop of epoll_event.

Notes take by Wangjiayi.
2018.5
*/
#ifndef _MESSAGE_
#define _MESSAGE_

#include<string>
#include<unordered_map>
#include<cmath>
#include<iostream>
#include"threadPool.hpp"
#include"room.hpp"

using namespace std;

extern rooms room;

class message;
class notification;
class rawMessage;

class message
{
protected:
    int type;
    string roomNo;
    int fd;
public:
    message(int _type,const string& _roomNo,int _fd):type(_type),roomNo(_roomNo),fd(_fd) {}
    virtual bool process()=0;
};

class request:public message
{
private:
    bool isOpen;
    double temperature;
    int wind;
public:
    request(const string &_roomNo,int _fd,bool _isOpen,double _temperature,int _wind):
        message(0,_roomNo,_fd),isOpen(_isOpen),temperature(_temperature),wind(_wind) {}
    bool process()
    {
        if(room.isPermitted(this->roomNo)){
            if(room.isExist(this->roomNo)){
                room.modify(this->roomNo,this->temperature,this->wind,this->isOpen);
            }
            else{
                room.insert(this->roomNo,roomInfo(this->roomNo,true,this->wind,this->temperature,25));
            }
        }
        return true;
    }
};

class notification:public message
{
private:
    double temperature;
public:
    notification(const string& _roomNo,int _fd,double _temperature):
        message(1,_roomNo,_fd),temperature(_temperature) {}
    bool process()
    {
        if(room.isExist(this->roomNo)){
            room.calculate(this->roomNo,this->temperature);
            room.scheduleRR(this->roomNo);
        }
        else
        {
            room.insert(this->roomNo,roomInfo(this->roomNo,true,0,25,temperature));
        }
        room.feedBack(this->roomNo,this->fd);
        return true;
    }
};

class rawMessage
{
private:
    string jsonText;
    int fd;
public:
    rawMessage(const string& _jsonText,int _fd):jsonText(_jsonText),fd(_fd) {}
    bool process()
    {
        //cout<<"tid:"<<pthread_self()<<endl;
        Json::Reader reader;
        Json::Value value;
        reader.parse(jsonText,value);
        //request
        if(value["type"].asInt()==0)
        {
            string roomNo=value["room"].asString();
            bool isOpen=value["switch"].asBool();
            double temperature=value["temperature"].asDouble();
            int wind=value["wind"].asInt();
            request tmp(roomNo,fd,isOpen,temperature,wind);
            tmp.process();
        }
        //notify
        else if(value["type"].asInt()==1)
        {
            string roomNo=value["room"].asString();
            double temperature=value["temperature"].asDouble();
            notification tmp(roomNo,fd,temperature);
            tmp.process();
        }
        return true;
    }
};

#endif
